﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Godot;
using Godot.Collections;

namespace VectorSurvivors.GameWorld;

/// <summary>
/// Skyrim-like inventory
/// </summary>
public class Inventory : IEnumerable<ItemStack>
{
    [Export] private List<ItemStack> _items;
    [Export] public int Size { get; protected set; } = -1;
    public int ItemsCount => _items.Count;
    public bool IsUnlimited => Size == -1;
    public bool IsFull => !IsUnlimited && ItemsCount >= Size;
    public bool ConfigLocked { get; protected set; } = false;
    
    public ItemStack this[int index] => _items[index];


    
    public Inventory(int size = 0)
    {
        if(size != 0)
            Configure(size);
    }
    
    public void LockConfig()
    {
        ConfigLocked = true;
    }

    public void Configure(int size)
    {
        if(ConfigLocked) return;
        Size = Mathf.Max(size, -1);
        ConfigLocked = true;
        _items = new();
    }

    public bool Add(ItemStack item)
    {
        if (IsFull) return false;
        _items.Add(item);
        item.CurrentInventory = this;
        return true;
    }

    public bool MoveTo(int index, Inventory other)
    {
        // get stack by id
        var stack = this[index];

        // trying to merge to existing stack in other inventory
        if (other.TryMerge(stack))
        {
            // if our stack is empty - remove it
            if (!stack.IsValid) return true;
        }
        
        // now try to put remaining stack to other inventory
        if (other.IsFull) return false;
        other.Add(stack);
        stack.CurrentInventory = other;
        RemoveAt(index);
        return true;
    }

    public bool TryMerge(ItemStack outerStack)
    {
        var innerStack = _items.FirstOrDefault(outerStack.CanMergeTo);
        if(innerStack is null) return false;
        outerStack.MergeTo(innerStack);
        return true;
    }

    public bool CanBeMerged(ItemStack stack)
    {
        return _items.Any(stack.CanMergeTo);
    }
    
    public void Remove(ItemStack item)
    {
        _items.Remove(item);
    }
    
    public void RemoveAt(int index)
    {
        _items.RemoveAt(index);
    }
    
    public void Clear()
    {
        _items.Clear();
    }
    
    public bool Contains(ItemStack item)
    {
        return _items.Contains(item);
    }
    
    public int IndexOf(ItemStack item)
    {
        return _items.IndexOf(item);
    }
    
    public void Insert(int index, ItemStack item)
    {
        _items.Insert(index, item);
    }


    public IEnumerator<ItemStack> GetEnumerator()
    {
        foreach (ItemStack item in _items)
        {
            yield return item;
        }
    }
    

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Notify_StackMerged(ItemStack stack)
    {
        if(!stack.IsValid)
            Remove(stack);
    }
}